import re
import selenium
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait


driver = webdriver.Chrome()
driver.maximize_window()                    # 窗口最大化
driver.implicitly_wait(6)                   # 隐式等待6s


# 功能: 用户登录
# 说明: 通过扫码方式手动登录(登录后显示的评论信息更全)
def bilibili_login():
    driver.get("https://www.bilibili.com")
    login_element = driver.find_element(By.CLASS_NAME, "header-login-entry")
    if login_element:
        login_element.click()

    while True:
        try:
            driver.find_element(By.CLASS_NAME, "bili-avatar")
            break
        except selenium.common.exceptions.NoSuchElementException:
            pass


# 功能: 根据url, 获取当前视频的所有评论信息
# 参数:
#   invite_url(str): 待访问的url地址
# 返回: 评论信息: [{"author": ***, "time": ***, "content": ***, "u": *, "d": *, "child": ["content": ***, ]}, ]
# 说明: 评论信息为列表, 列表中的元素为字典, content字段记录评论信息, child字段记录对评论信息的评论或回复
#       author: 作者, time: 评论时间, content: 评论内容, u: 喜欢, d: 不喜欢, child: 子评论
def get_comment_by_url(invite_url):
    ret = []
    if not invite_url == driver.current_url:
        driver.get(invite_url)

    # 获取评论列表, 一次最多只能获取20条评论, 需要通过滚动鼠标滚轮刷新
    WebDriverWait(driver, 30).until(lambda _driver: _driver.find_element(By.CLASS_NAME, "reply-list"))

    reply_items = driver.find_elements(By.CLASS_NAME, "reply-item")
    for reply_item in reply_items:
        root_reply_container = reply_item.find_element(By.CLASS_NAME, "root-reply-container")

        sub_reply_container = reply_item.find_element(By.CLASS_NAME, "sub-reply-container")
        sub_reply_items = sub_reply_container.find_elements(By.CLASS_NAME, "sub-reply-item")

        # 获取子评论信息
        child = []
        for sub_reply_item in sub_reply_items:
            child.append({
                "author": sub_reply_item.find_element(By.CLASS_NAME, "sub-user-name").text,
                "time": sub_reply_item.find_element(By.CLASS_NAME, "sub-reply-time").text,
                "content": sub_reply_item.find_element(By.CLASS_NAME, "reply-content").text,
                "u": sub_reply_item.find_element(By.CLASS_NAME, "sub-reply-like").text,
                "d": sub_reply_item.find_element(By.CLASS_NAME, "sub-reply-dislike").text,
            })

        # 获取评论信息
        ret.append({
            "author": root_reply_container.find_element(By.CLASS_NAME, "user-name").text,
            "time": root_reply_container.find_element(By.CLASS_NAME, "reply-time").text,
            "content": root_reply_container.find_element(By.CLASS_NAME, "reply-content").text,
            "u": root_reply_container.find_element(By.CLASS_NAME, "reply-like").text,
            "d": root_reply_container.find_element(By.CLASS_NAME, "reply-dislike").text,
            "child": child
        })
    # Todo: 通过循环滚动鼠标滚轮, 达到刷新并获取所有评论信息的效果
    driver.execute_script("window.scrollTo(0, 2000)")       # 向下滑动2000个像素

    return ret


# 功能: 根据url, 获取当前视频的所有弹幕信息
# 参数:
#   invite_url(str): 待访问的url地址
# 返回: 弹幕信息: [(time, content), ]
# 说明: 弹幕信息为列表, 列表中的元素为元组, time为弹幕时间, content为弹幕内容
def get_barrage_by_url(invite_url):
    if not invite_url == driver.current_url:
        driver.get(invite_url)

    bv_info = re.findall(re.compile(r"/video/(.*?)/"), invite_url)
    if bv_info:
        return get_barrage_by_bv(bv_info[0])


# 功能: 根据bv, 获取当前视频的所有弹幕信息
# 参数:
#   invite_url(str): 待访问的url地址
# 返回: 弹幕信息: [(time, content), ]
# 说明: 弹幕信息为列表, 列表中的元素为元组, time为弹幕时间, content为弹幕内容
def get_barrage_by_bv(bv):
    driver.execute_script("window.open('', '_blank');")
    handles = driver.window_handles
    driver.switch_to.window(handles[-1])        # 切换到新的标签页
    driver.get(f"https://api.bilibili.com/x/player/pagelist?bvid={bv}&jsonp=jsonp")
    cid = re.findall(re.compile(r'"cid":(\d+),'), driver.page_source)[0]

    driver.get(f"https://api.bilibili.com/x/v1/dm/list.so?oid={cid}")
    ret = re.findall(re.compile(r'\d+?">(.*?)</d>'), driver.page_source)
    driver.close()                              # 关闭当前标签页
    driver.switch_to.window(handles[0])         # 切换回原标签页

    return ret


if __name__ == "__main__":
    bilibili_login()
    url = "https://www.bilibili.com/video/BV15x4y117AN"
    print(get_comment_by_url(url))
    pass
